import { Mesh, Vector2, Vector3, Raycaster, CatmullRomCurve3, TubeGeometry, MeshPhongMaterial, SphereGeometry, MeshBasicMaterial } from "three"

export default class Pipes {
    constructor(viewer) {
        this.viewer = viewer
        this.mouse = undefined
        this.raycaster = undefined

        this.spheresIndex = 0; // 管道端点
        this.spheres = [];
        this.pipeRecord = [];

        this.clickListener = undefined
        this.lListener = undefined

        this.#init()
    }

    #init() {
        this.mouse = new Vector2(); // 创建鼠标坐标
        this.raycaster = new Raycaster(); // 创建射线
    }

    addClickListener() {
        this.clickListener = this.#clickEvent.bind(this, this)
        this.viewer.viewerDom.addEventListener("click", this.clickListener)
    }

    addLListener() {
        this.lListener = this.#lEvent.bind(this, this)
        window.addEventListener("keydown", this.lListener)
    }

    removeLListener() {
        window.removeEventListener("keydown", this.lListener)
    }

    removeClickListener() {
        this.viewer.viewerDom.removeEventListener("click", this.clickListener)
    }

    /**
    * 单击编辑管道
    */
    #clickEvent(that, event) {
        const { clientHeight, clientWidth } = that.viewer.viewerDom;
        // 计算鼠标在屏幕上的坐标
        that.mouse.x = (event.offsetX / clientWidth) * 2 - 1;
        that.mouse.y = -(event.offsetY / clientHeight) * 2 + 1;

        that.raycaster.setFromCamera(that.mouse, that.viewer.camera); // 设置射线的起点和终点
        const intersects = that.raycaster.intersectObjects(that.viewer.objects, true); // 检测射线与模型是否相交
        if (intersects.length > 0 && intersects[0]) {
            if (intersects[0].object instanceof Mesh) {
                that.spheres[that.spheresIndex].position.copy(intersects[0].point);
                that.spheres[that.spheresIndex].scale.set(1, 1, 1);
                that.spheresIndex = (that.spheresIndex + 1) % that.spheres.length;
            }
        }
    }

    #lEvent(that, event) {
        if (event.key === "l") {
            const points = this.#calPipe(that.spheres[0].position, that.spheres[1].position);

            that.loadPipe(points).then(() => {
                that.#emptyPipe();
            });
        }
    }

    /**
     * 添加管道（开启事件：点击选端点）
     */
    addPipe() {
        this.#emptyPipe();
        this.addClickListener()
        this.addLListener()

        const sphereGeometry = new SphereGeometry(0.5, 32, 32);
        const sphereMaterial = new MeshBasicMaterial({ color: 0xff0000 });
        for (let i = 0; i < 2; i++) {
            const sphere = new Mesh(sphereGeometry, sphereMaterial);
            this.viewer.scene.add(sphere);
            this.spheres.push(sphere);
        }
    };

    /**
     * 取消管道编辑
     */
    cancelPipe() {
        this.#emptyPipe();
    };

    /**
     * 清空管道相关（清除红点、关闭事件）
     */
    #emptyPipe() {
        for (let i = 0; i < this.spheres.length; i++) {
            this.viewer.scene.remove(this.spheres[i]);
        }
        this.spheres.splice(0, this.spheres.length);
        this.removeClickListener()
        this.removeLListener()
    };

    /**
    * 添加管道
    */
    loadPipe(points) {
        return new Promise(async (reslove) => {
            const pointsV3 = points.map(({ x, y, z }) => new Vector3(x, y, z));
            const curve = new CatmullRomCurve3(pointsV3);
            curve.curveType = "centripetal";
            curve.closed = false;
            const geometry1 = new TubeGeometry(curve, 32, 0.4, 4, false);
            const material1 = new MeshPhongMaterial({ color: 0x043365 });
            const mesh1 = new Mesh(geometry1, material1);
            
            this.viewer.scene.add(mesh1);
            this.viewer.pipes.push(mesh1);
            this.pipeRecord.push({ uuid: mesh1.uuid, points });

            reslove(true)
        })
    }

    /**
    * 计算管道
    */
    #calPipe(p1, p2) {
        let points = [
            { x: p1.x, y: p1.y, z: p1.z },
            { x: p1.x, y: p1.y, z: p2.z },
            { x: p2.x, y: p1.y, z: p2.z },
            { x: p2.x, y: p2.y, z: p2.z },
        ];
        return points;
    };
}